// GeometricTransformer.cpp
#include "GeometricTransformer.h"
#include <stdexcept>

bool GeometricTransformer::IsValidImage(const cv::Mat& img) {
    return !img.empty() && img.cols > 0 && img.rows > 0;
}

// ========= 缩放 ==========
/*
1、const cv::Mat& src
    输入图像（源图像），使用 const & 避免拷贝，提高性能。
    类型是 OpenCV 的矩阵 cv::Mat，可以是灰度图、彩色图或其他通道类型。

2、double scaleX
    水平方向的缩放系数。
    例如 scaleX = 2.0 表示图像宽度放大 2 倍，scaleX = 0.5 表示缩小一半。

3、double scaleY
    垂直方向的缩放系数。
    类似 scaleX，控制图像高度缩放比例。

4、int interpolation
    插值方法，用于计算缩放后的像素值。
    常用值：
        cv::INTER_NEAREST：最邻近插值，速度快但可能有锯齿
        cv::INTER_LINEAR：双线性插值（默认），平滑
        cv::INTER_CUBIC：三次插值，更平滑，但慢
        cv::INTER_LANCZOS4：Lanczos 插值，质量最高但最慢
 * */
cv::Mat GeometricTransformer::Scale(const cv::Mat& src,
                                    double scaleX,
                                    double scaleY,
                                    int interpolation) {
    if (!IsValidImage(src)) throw std::invalid_argument("Scale: empty input image.");
    if (scaleX <= 0.0 || scaleY <= 0.0) throw std::invalid_argument("Scale: scaleX/scaleY must be > 0.");
    cv::Mat dst;
    cv::resize(src, dst, cv::Size(), scaleX, scaleY, interpolation);
    return dst;
}

cv::Mat GeometricTransformer::ScaleTo(const cv::Mat& src,
                                      const cv::Size& dstSize,
                                      int interpolation) {
    if (!IsValidImage(src)) throw std::invalid_argument("ScaleTo: empty input image.");
    if (dstSize.width <= 0 || dstSize.height <= 0) throw std::invalid_argument("ScaleTo: invalid dstSize.");
    cv::Mat dst;
    cv::resize(src, dst, dstSize, 0, 0, interpolation);
    return dst;
}

// ====== 平移 ======
cv::Matx23f GeometricTransformer::MakeTranslationMatrix(double shiftX, double shiftY) {
    return cv::Matx23f(1.f, 0.f, static_cast<float>(shiftX),
                       0.f, 1.f, static_cast<float>(shiftY));
}

cv::Mat GeometricTransformer::Translate(const cv::Mat& src,
                                        double shiftX,
                                        double shiftY,
                                        cv::Size dstSize,
                                        int interpolation,
                                        int borderMode,
                                        const cv::Scalar& borderValue) {
    if (!IsValidImage(src)) throw std::invalid_argument("Translate: empty input image.");
    cv::Mat dst;
    cv::Matx23f M = MakeTranslationMatrix(shiftX, shiftY);
    if (dstSize.width <= 0 || dstSize.height <= 0) dstSize = src.size();
    cv::warpAffine(src, dst, M, dstSize, interpolation, borderMode, borderValue);
    return dst;
}

// ====== 旋转 ========================================================================================
cv::Matx23f GeometricTransformer::MakeRotationMatrix(double angleDeg, cv::Point2f center, double scale) {
    cv::Mat M = cv::getRotationMatrix2D(center, angleDeg, scale); // 2x3
    cv::Matx23f Mx;
    M.copyTo(Mx);
    return Mx;
}

cv::Size GeometricTransformer::ComputeRotatedBounds(
        const cv::Mat& src,
        const cv::Matx23f& M,
        cv::Matx23f& M_adjusted)
{
    // 将 2x3 扩展为 3x3
    cv::Matx33f M3(M(0,0), M(0,1), M(0,2),
                   M(1,0), M(1,1), M(1,2),
                   0.f,    0.f,    1.f);

    // 原始图像四个角
    std::vector<cv::Point2f> corners = {
            {0.f, 0.f},
            {static_cast<float>(src.cols), 0.f},
            {static_cast<float>(src.cols), static_cast<float>(src.rows)},
            {0.f, static_cast<float>(src.rows)}
    };

    float minx = 1e9f, miny = 1e9f, maxx = -1e9f, maxy = -1e9f;
    for (auto& p : corners) {
        cv::Vec3f hp(p.x, p.y, 1.f);
        cv::Vec3f tp = M3 * hp;  // 用 3x3 矩阵变换点
        float x = tp[0], y = tp[1];
        minx = std::min(minx, x); miny = std::min(miny, y);
        maxx = std::max(maxx, x); maxy = std::max(maxy, y);
    }

    int newW = static_cast<int>(std::ceil(maxx - minx));
    int newH = static_cast<int>(std::ceil(maxy - miny));

    // 平移矩阵 (3x3)
    cv::Matx33f S3(1.f, 0.f, -minx,
                   0.f, 1.f, -miny,
                   0.f, 0.f, 1.f);

    // 调整后的矩阵 = 平移矩阵 * 原旋转矩阵
    cv::Matx33f Adj = S3 * M3;

    // 取前两行转回 2x3
    M_adjusted = cv::Matx23f(Adj(0,0), Adj(0,1), Adj(0,2),
                             Adj(1,0), Adj(1,1), Adj(1,2));

    return cv::Size(newW, newH);
}


cv::Mat GeometricTransformer::Rotate(const cv::Mat& src,
                                     double angleDeg,
                                     double scale,
                                     int interpolation,
                                     int borderMode,
                                     const cv::Scalar& borderValue) {
    if (!IsValidImage(src)) throw std::invalid_argument("Rotate: empty input image.");
    cv::Point2f center(src.cols * 0.5f, src.rows * 0.5f);
    cv::Matx23f M = MakeRotationMatrix(angleDeg, center, scale);
    cv::Mat dst;
    cv::warpAffine(src, dst, M, src.size(), interpolation, borderMode, borderValue);
    return dst;
}

cv::Mat GeometricTransformer::RotateAround(const cv::Mat& src,
                                           double angleDeg,
                                           cv::Point2f center,
                                           double scale,
                                           bool expandCanvas,
                                           int interpolation,
                                           int borderMode,
                                           const cv::Scalar& borderValue) {
    if (!IsValidImage(src)) throw std::invalid_argument("RotateAround: empty input image.");
    cv::Matx23f M = MakeRotationMatrix(angleDeg, center, scale);
    cv::Mat dst;

    if (!expandCanvas) {
        cv::warpAffine(src, dst, M, src.size(), interpolation, borderMode, borderValue);
        return dst;
    } else {
        cv::Matx23f M_adj;
        cv::Size newSize = ComputeRotatedBounds(src, M, M_adj);
        cv::warpAffine(src, dst, M_adj, newSize, interpolation, borderMode, borderValue);
        return dst;
    }
}

// ====== 仿射变换 ======
cv::Mat GeometricTransformer::Affine(const cv::Mat& src,
                                     const std::vector<cv::Point2f>& srcPts,
                                     const std::vector<cv::Point2f>& dstPts,
                                     cv::Size dstSize,
                                     int interpolation,
                                     int borderMode,
                                     const cv::Scalar& borderValue) {
    if (!IsValidImage(src)) throw std::invalid_argument("Affine: empty input image.");
    if (srcPts.size() != 3 || dstPts.size() != 3) throw std::invalid_argument("Affine: need 3 srcPts and 3 dstPts.");
    cv::Mat M = cv::getAffineTransform(srcPts, dstPts); // 2x3
    if (dstSize.width <= 0 || dstSize.height <= 0) dstSize = src.size();
    cv::Mat dst;
    cv::warpAffine(src, dst, M, dstSize, interpolation, borderMode, borderValue);
    return dst;
}

cv::Mat GeometricTransformer::AffineWithMatrix(const cv::Mat& src,
                                               const cv::Mat& M2x3,
                                               cv::Size dstSize,
                                               int interpolation,
                                               int borderMode,
                                               const cv::Scalar& borderValue) {
    if (!IsValidImage(src)) throw std::invalid_argument("AffineWithMatrix: empty input image.");
    if (M2x3.empty() || M2x3.rows != 2 || M2x3.cols != 3) throw std::invalid_argument("AffineWithMatrix: M must be 2x3.");
    if (dstSize.width <= 0 || dstSize.height <= 0) dstSize = src.size();
    cv::Mat dst;
    cv::warpAffine(src, dst, M2x3, dstSize, interpolation, borderMode, borderValue);
    return dst;
}

// ====== 透视变换 ======
cv::Matx33f GeometricTransformer::MakePerspectiveMatrix(const std::vector<cv::Point2f>& srcPts,
                                                        const std::vector<cv::Point2f>& dstPts) {
    if (srcPts.size() != 4 || dstPts.size() != 4) throw std::invalid_argument("MakePerspectiveMatrix: need 4 points.");
    cv::Mat H = cv::getPerspectiveTransform(srcPts, dstPts); // 3x3
    cv::Matx33f Hx;
    H.copyTo(Hx);
    return Hx;
}

cv::Mat GeometricTransformer::Perspective(const cv::Mat& src,
                                          const std::vector<cv::Point2f>& srcPts,
                                          const std::vector<cv::Point2f>& dstPts,
                                          cv::Size dstSize,
                                          int interpolation,
                                          int borderMode,
                                          const cv::Scalar& borderValue) {
    if (!IsValidImage(src)) throw std::invalid_argument("Perspective: empty input image.");
    if (srcPts.size() != 4 || dstPts.size() != 4) throw std::invalid_argument("Perspective: need 4 srcPts and 4 dstPts.");
    if (dstSize.width <= 0 || dstSize.height <= 0) throw std::invalid_argument("Perspective: dstSize must be specified.");
    cv::Mat H = cv::getPerspectiveTransform(srcPts, dstPts);
    cv::Mat dst;
    cv::warpPerspective(src, dst, H, dstSize, interpolation, borderMode, borderValue);
    return dst;
}

cv::Mat GeometricTransformer::PerspectiveWithMatrix(const cv::Mat& src,
                                                    const cv::Mat& H3x3,
                                                    cv::Size dstSize,
                                                    int interpolation,
                                                    int borderMode,
                                                    const cv::Scalar& borderValue) {
    if (!IsValidImage(src)) throw std::invalid_argument("PerspectiveWithMatrix: empty input image.");
    if (H3x3.empty() || H3x3.rows != 3 || H3x3.cols != 3) throw std::invalid_argument("PerspectiveWithMatrix: H must be 3x3.");
    if (dstSize.width <= 0 || dstSize.height <= 0) throw std::invalid_argument("PerspectiveWithMatrix: dstSize must be specified.");
    cv::Mat dst;
    cv::warpPerspective(src, dst, H3x3, dstSize, interpolation, borderMode, borderValue);
    return dst;
}
